using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using AppEventUtils;
using System;


public static class CoroutineHelper
{
    public static IEnumerator WaitUntilCondition(Func<bool> condition)
    {
        while (!condition())
        {
            yield return null;
        }
    }
}


public enum GameEventTypeEnum
{
    Init,
    Start,
    
    Animation,
    
    OnLoadScene,
    
    // --- 场景变换事件
    StartBattle,
    EndBattle,
    StartStore,
    EndStore,
    
    // --- 酒馆事件
    OnManualRefreshStore,
    OnRefreshStore,
    
    // --- 战斗事件
    OnAttack,
    OnKill,
    OnHit,
    OnBeHurt,
    OnTooMoreSoldier,  // 挤死流
    
    // --- 酒馆事件
    
    End,
    Exit,
}


public class EventManager : MonoBehaviour
{
    public bool debug = false;
    
    public int room_id = 1;
    
    public static EventManager instance;

    private void Awake()
    {
        if (instance != null)
            Destroy(this);
        instance = this;
    }
    
    public Dictionary<string, OrderedEventList> event_dc = new Dictionary<string, OrderedEventList>();

    public OrderedEventList animation_event_ls => event_dc[GameEventTypeEnum.Animation.ToString()];

    public OrderedEventBase current_event_i;
    
    IEnumerator animation_runner()
    {
        // BdPackage.BdUtils.log("--- start animation_runner 0");

        while (!GameController.instance.is_ready)
            yield return new WaitForSeconds(0.2f);

        // BdPackage.BdUtils.log("--- start animation_runner 1");
        while (true)
        {
            yield return new WaitForSeconds(0.01f);

            if (animation_event_ls.Count <= 0)
            {
                continue;
            }

            var eventItem = animation_event_ls.Events[0];
            current_event_i = eventItem;

            // BdPackage.BdUtils.log($"--- animation_runner --- eventItem.EventName: {eventItem.EventName}");
            
            float start_time = Time.time;

            while (Time.time - start_time <= eventItem.force_wait_time)
            {
                yield return new WaitForSeconds(0.01f);
            }
            
            if (eventItem.block)
            {
                while (!eventItem.finished && Time.time - start_time <= eventItem.wait_time)
                {
                    yield return new WaitForSeconds(0.1f);
                }
                
            }
            
            eventItem.Run();

            if (eventItem.block)
            {
                start_time = Time.time;
                while (!eventItem.finished || Time.time - start_time <= eventItem.min_running_time)
                {
                    yield return new WaitForSeconds(0.1f);
                    // BdPackage.BdUtils.log($"--- animation_runner.count[{animation_event_ls.Count}] --- event[{eventItem.EventName}] inner running!");
                
                    if (Time.time - start_time >= eventItem.max_running_time)
                        break;
                    
                }
                
                animation_event_ls.RemoveEventById(eventItem.EventId);
            }
            else
            {
                animation_event_ls.RemoveEventById(eventItem.EventId);    
            }
        }
        // yield return null;
    }

    void Start()
    {
        foreach (var eventType in Enum.GetValues(typeof(GameEventTypeEnum)))
        {
            event_dc.Add(eventType.ToString(), new OrderedEventList(room_id));
        }
        
        foreach (var eventType in Enum.GetValues(typeof(CardEventTypeEnum)))
        {
            event_dc.Add(eventType.ToString(), new OrderedEventList(room_id));
        }
        
        // --- 增加一个测试数据
        
        var eventData = new Dictionary<string, object>()
        {
            { "a", 123 },
            { "b", "bbb" },
            { "c", "ccc" },
        };
        add_event(eventType: GameEventTypeEnum.Init.ToString(), eventData: eventData, event_instance: on_init, removeWhenRunOnce: false);

        StartCoroutine(animation_runner());
    }

    public OrderedEventBase add_animation_event(
        object eventData = null,
        float ordering = OrderingTypes.default_level,
        string eventName = null,
        bool removeWhenRunOnce = true,
        EventHandler<EventArgs> event_instance = null,
        OrderedEventBase.EventCallback event_callback = null
    )
    {
        return add_event(GameEventTypeEnum.Animation.ToString(),
            eventData: eventData,
            ordering: ordering,
            eventName: eventName,
            removeWhenRunOnce: removeWhenRunOnce,
            event_instance: event_instance,
            event_callback: event_callback);
    }

    /// <summary>
    /// 增加事件
    /// </summary>
    /// <param name="eventType">对应`event_dc`的key</param>
    /// <param name="eventData">事件数据</param>
    /// <param name="ordering">排序权重, 升序</param>
    /// <param name="eventName">事件名</param>
    /// <param name="removeWhenRunOnce">运行一次后移除事件</param>
    /// <param name="event_instance">回调函数</param>
    /// <param name="event_callback">委托回调函数</param>
    public OrderedEventBase add_event(string eventType,
        // Dictionary<string, object> eventData,
        object eventData = null,
        float ordering = OrderingTypes.default_level,
        string eventName = "asdf",
        bool removeWhenRunOnce = true,
        EventHandler<EventArgs> event_instance = null,
        OrderedEventBase.EventCallback event_callback = null
        )
    {

        BdUtils.assert(event_dc.ContainsKey(eventType), "event_dc not contains eventType: " + eventType);        
        
        // var _eventData = BdUtils.conv_obj_to_json(eventData);
        // BdPackage.BdUtils.log($"========= eventName 1: {eventName}, bool: {BdPackage.BdUtils.conv_to_bool(eventName)}");

        eventName = BdUtils.conv_to_bool(eventName) ? eventName : $"Event[{eventType}]";
        // BdPackage.BdUtils.log($"========= eventName 2: {eventName}, bool: {BdPackage.BdUtils.conv_to_bool(eventName)}");

        var event_i = new OrderedEventBase(
            roomId: room_id,
            eventType: eventType,
            eventName: eventName,
            ordering: ordering,
            // eventData: _eventData,
            eventData: eventData,
            removeWhenRunOnce: removeWhenRunOnce,
            event_instance: event_instance,
            event_callback: event_callback
            );
        
        event_dc[eventType].AddEvent(event_i);
        return event_i;
    }

    public OrderedEventBase add_event(OrderedEventBase event_obj, string event_type = null)
    {
        if (event_type == null)
            event_type = event_obj.EventType;

        event_dc[event_type].AddEvent(event_obj);
        return event_obj;
    }
    
    public void remove_event(OrderedEventBase event_obj, string event_type = null)
    {
        if (event_type == null)
            event_type = event_obj.EventType;
        
        // if (has_event(event_obj, event_type))
        instance.event_dc[event_type].RemoveEvent(event_obj);
    }

    public bool has_event(OrderedEventBase event_obj, string event_type = null)
    {
        if (event_type == null)
            event_type = event_obj.EventType;
        return instance.event_dc[event_type].HasEvent(event_obj);
    }

    public (OrderedEventBase, Dictionary<string, object>) get_event_obj_and_data(object sender)
    {
        var event_obj = (OrderedEventBase)sender;
        var event_data = BdUtils.conv_json_to_dict(event_obj.EventData);
        return (event_obj, event_data);
    }
    
    public (OrderedEventBase, object) get_original_event_obj_and_data(object sender)
    {
        var event_obj = (OrderedEventBase)sender;
        var event_data = event_obj.EventData;
        return (event_obj, event_data);
    }

    public void on_init(object sender, EventArgs args)
    {
        var (event_obj, event_data) = get_event_obj_and_data(sender);

        BdPackage.BdUtils.log("=== on_init --- ", sender, " --- event_obj.EventData:", event_obj.EventData);

        event_data["a"] = BdUtils.conv_to_int(event_data["a"]) - 1;
        event_obj.EventData = event_data;
        // event_obj.EventData = BdUtils.conv_obj_to_json(event_data);
        
        // remove_event(event_obj);
    }

    // 运行一个 event_type 绑定的所有事件
    public void RunAllEventsInOneType(string event_type, string msg = null)
    {
        FireEvent(event_type, msg);
    }

    // 触发一个 event_type 绑定的所有事件
    public void FireEvent(string event_type, string msg = null)
    {
        BdPackage.BdUtils.assert(event_dc.ContainsKey(event_type), "event_dc not found event_type: " + event_type);
        event_dc[event_type].RunAllEvents(msg);
    }
    
    private void OnGUI()
    {
        if (!debug)
            return;
        
        if (GUILayout.Button("call [init] event"))
        {
            RunAllEventsInOneType(GameEventTypeEnum.Init.ToString());
        }
    }

    private string blocking_event_name = "blocking_event";
    public void add_blocking_event(bool block = true, float max_running_time = 5f, float wait_time = 0.01f)
    {
        var event_i = add_animation_event(eventName: blocking_event_name, removeWhenRunOnce: true);
        event_i.block = block;
        event_i.max_running_time = max_running_time;
        event_i.wait_time = wait_time;
        
        event_i.event_callback = (sender, args) =>
        {
            StartCoroutine(wait_for_animation_runner_completion(event_i));

            return "";
        };
    }

    IEnumerator wait_for_animation_runner_completion(OrderedEventBase event_i)
    {
        // CoroutineHelper.WaitUntilCondition(() => animation_event_ls.Count == 1)

        // while (animation_event_ls.Count > 1 && current_event_i.EventName != blocking_event_name)
        while (current_event_i.EventName != blocking_event_name)
        {
            yield return null;
        }
        event_i.finished = true;
    }
}
